From 1571e18e4a69da146652d88dd89d38e3d746abc3 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 23 Jun 2023 10:17:45 +0200 Subject: [PATCH] bridge: add support for configuring extra tagged vlans on member devices This can be used to allow trunking dynamically allocated VLANs into a specific member port. In order to use this, add a device section for the member port with the 'vlan' array option, which can contain vlan numbers or ranges. Signed-off-by: Felix Fietkau --- bridge.c | 20 ++++++++++++++++---- device.c | 43 ++++++++++++++++++++++++++++++++++++++++++- device.h | 5 +++++ system-dummy.c | 6 +++--- system-linux.c | 13 ++++++++++++- system.h | 2 +- 6 files changed, 79 insertions(+), 10 deletions(-) diff --git a/bridge.c b/bridge.c index c8cfca4..0338f14 100644 --- a/bridge.c +++ b/bridge.c @@ -206,7 +206,7 @@ __bridge_set_member_vlan(struct bridge_member *bm, struct bridge_vlan *vlan, if (bm->pvid == vlan->vid) flags |= BRVLAN_F_PVID; - system_bridge_vlan(port->ifname, vlan->vid, add, flags); + system_bridge_vlan(port->ifname, vlan->vid, -1, add, flags); } static void @@ -233,7 +233,7 @@ bridge_set_local_vlan(struct bridge_state *bst, struct bridge_vlan *vlan, bool a if (!vlan->local && add) return; - system_bridge_vlan(bst->dev.ifname, vlan->vid, add, BRVLAN_F_SELF); + system_bridge_vlan(bst->dev.ifname, vlan->vid, -1, add, BRVLAN_F_SELF); } static void @@ -361,7 +361,7 @@ bridge_enable_interface(struct bridge_state *bst) if (bst->has_vlans) { /* delete default VLAN 1 */ - system_bridge_vlan(bst->dev.ifname, 1, false, BRVLAN_F_SELF); + system_bridge_vlan(bst->dev.ifname, 1, -1, false, BRVLAN_F_SELF); bridge_set_local_vlans(bst, true); } @@ -394,6 +394,17 @@ bridge_disable_interface(struct bridge_state *bst) bst->active = false; } +static void +bridge_member_add_extra_vlans(struct bridge_member *bm) +{ + struct device *dev = bm->dev.dev; + int i; + + for (i = 0; i < dev->n_extra_vlan; i++) + system_bridge_vlan(dev->ifname, dev->extra_vlan[i].start, + dev->extra_vlan[i].end, true, 0); +} + static int bridge_enable_member(struct bridge_member *bm) { @@ -435,8 +446,9 @@ bridge_enable_member(struct bridge_member *bm) bm->active = true; if (bst->has_vlans) { /* delete default VLAN 1 */ - system_bridge_vlan(bm->dev.dev->ifname, 1, false, 0); + system_bridge_vlan(bm->dev.dev->ifname, 1, -1, false, 0); + bridge_member_add_extra_vlans(bm); vlist_for_each_element(&bst->dev.vlans, vlan, node) bridge_set_member_vlan(bm, vlan, true); } diff --git a/device.c b/device.c index 1e89219..e98c77e 100644 --- a/device.c +++ b/device.c @@ -63,6 +63,7 @@ static const struct blobmsg_policy dev_attrs[__DEV_ATTR_MAX] = { [DEV_ATTR_AUTH] = { .name = "auth", .type = BLOBMSG_TYPE_BOOL }, [DEV_ATTR_SPEED] = { .name = "speed", .type = BLOBMSG_TYPE_INT32 }, [DEV_ATTR_DUPLEX] = { .name = "duplex", .type = BLOBMSG_TYPE_BOOL }, + [DEV_ATTR_VLAN] = { .name = "vlan", .type = BLOBMSG_TYPE_ARRAY }, }; const struct uci_blob_param_list device_attr_list = { @@ -283,6 +284,45 @@ device_merge_settings(struct device *dev, struct device_settings *n) n->flags = s->flags | os->flags | os->valid_flags; } +static void +device_add_extra_vlan(struct device *dev, const char *val) +{ + unsigned long cur_start, cur_end; + char *sep; + + cur_start = strtoul(val, &sep, 0); + cur_end = cur_start; + + if (*sep == '-') + cur_end = strtoul(sep + 1, &sep, 0); + if (*sep || cur_end < cur_start) + return; + + dev->extra_vlan[dev->n_extra_vlan].start = cur_start; + dev->extra_vlan[dev->n_extra_vlan].end = cur_end; + dev->n_extra_vlan++; +} + +static void +device_set_extra_vlans(struct device *dev, struct blob_attr *data) +{ + struct blob_attr *cur; + int n_vlans; + size_t rem; + + dev->n_extra_vlan = 0; + if (!data) + return; + + n_vlans = blobmsg_check_array(data, BLOBMSG_TYPE_STRING); + if (n_vlans < 1) + return; + + dev->extra_vlan = realloc(dev->extra_vlan, n_vlans * sizeof(*dev->extra_vlan)); + blobmsg_for_each_attr(cur, data, rem) + device_add_extra_vlan(dev, blobmsg_get_string(cur)); +} + void device_init_settings(struct device *dev, struct blob_attr **tb) { @@ -463,7 +503,7 @@ device_init_settings(struct device *dev, struct blob_attr **tb) s->duplex = blobmsg_get_bool(cur); s->flags |= DEV_OPT_DUPLEX; } - + device_set_extra_vlans(dev, tb[DEV_ATTR_VLAN]); device_set_disabled(dev, disabled); } @@ -891,6 +931,7 @@ device_free(struct device *dev) __devlock++; free(dev->config); device_cleanup(dev); + free(dev->extra_vlan); dev->type->free(dev); __devlock--; } diff --git a/device.h b/device.h index 6751628..c3e38ac 100644 --- a/device.h +++ b/device.h @@ -62,6 +62,7 @@ enum { DEV_ATTR_AUTH, DEV_ATTR_SPEED, DEV_ATTR_DUPLEX, + DEV_ATTR_VLAN, __DEV_ATTR_MAX, }; @@ -248,6 +249,10 @@ struct device { bool bpdu_filter; struct interface *config_iface; + struct { + uint16_t start, end; + } *extra_vlan; + int n_extra_vlan; /* set interface up or down */ device_state_cb set_state; diff --git a/system-dummy.c b/system-dummy.c index b13bc87..84382b3 100644 --- a/system-dummy.c +++ b/system-dummy.c @@ -55,12 +55,12 @@ int system_bridge_delif(struct device *bridge, struct device *dev) return 0; } -int system_bridge_vlan(const char *iface, uint16_t vid, bool add, unsigned int vflags) +int system_bridge_vlan(const char *iface, uint16_t vid, int16_t vid_end, bool add, unsigned int vflags) { - D(SYSTEM, "brctl vlan %s %s %s vid=%d pvid=%d untag=%d\n", + D(SYSTEM, "brctl vlan %s %s %s vid=%d vid_end=%d pvid=%d untag=%d\n", add ? "add" : "remove", (vflags & BRVLAN_F_SELF) ? "self" : "master", - iface, vid, + iface, vid, vid_end, !!(vflags & BRVLAN_F_PVID), !!(vflags & BRVLAN_F_UNTAGGED)); return 0; diff --git a/system-linux.c b/system-linux.c index cc15537..0760e73 100644 --- a/system-linux.c +++ b/system-linux.c @@ -985,7 +985,7 @@ int system_bridge_delif(struct device *bridge, struct device *dev) return system_bridge_if(bridge->ifname, dev, SIOCBRDELIF, NULL); } -int system_bridge_vlan(const char *iface, uint16_t vid, bool add, unsigned int vflags) +int system_bridge_vlan(const char *iface, uint16_t vid, int16_t vid_end, bool add, unsigned int vflags) { struct bridge_vlan_info vinfo = { .vid = vid, }; unsigned short flags = 0; @@ -1020,7 +1020,18 @@ int system_bridge_vlan(const char *iface, uint16_t vid, bool add, unsigned int v if (flags) nla_put_u16(nlm, IFLA_BRIDGE_FLAGS, flags); + if (vid_end > vid) + vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN; + nla_put(nlm, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo); + + if (vid_end > vid) { + vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN; + vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END; + vinfo.vid = vid_end; + nla_put(nlm, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo); + } + nla_nest_end(nlm, afspec); return system_rtnl_call(nlm); diff --git a/system.h b/system.h index 1f7037d..19aafa4 100644 --- a/system.h +++ b/system.h @@ -246,7 +246,7 @@ int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg); int system_bridge_delbr(struct device *bridge); int system_bridge_addif(struct device *bridge, struct device *dev); int system_bridge_delif(struct device *bridge, struct device *dev); -int system_bridge_vlan(const char *iface, uint16_t vid, bool add, unsigned int vflags); +int system_bridge_vlan(const char *iface, uint16_t vid, int16_t vid_end, bool add, unsigned int vflags); int system_bridge_vlan_check(struct device *dev, char *ifname); void system_bridge_set_stp_state(struct device *dev, bool val); -- 2.30.2